/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the ViewMyCurrency extension.
 *
 * The Initial Developer of the Original Code is
 * Will Moffat.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

//dump("// $Id: WILL_ViewMyCurrencyGui.js,v 1.79 2007/03/19 10:54:29 Will Exp $;\n");

/////////////////////
// ** GUI STUFF ** //
/////////////////////
VMC.popupConvertManager = null;

VMC.ViewMyCurrencyGui = function() {
  var self = this;
  var LABEL_POPUP_CONVERT='ViewMyCurrency: ';
  var gMouse = null; // set by registerViewMyCurrency

  this._viewMyCurrency = null; //set by register func
  this._installStatus  = null;
  this._prefManager     = null; //set by registerViewMyCurrency

  this.registerViewMyCurrency      = registerViewMyCurrency;
  this.doContentMenuPopup          = doContentMenuPopup;
  this.doHideSource                = doHideSource;
  this.doToggleConvert             = doToggleConvert;
  this.doUpdateStatus              = doUpdateStatus;
  this.doStatusbarpanelClicked     = doStatusbarpanelClicked;
  this.doContextMenu               = doContextMenu;
  this.doOpenOptionsDialog         = doOpenOptionsDialog;
  this.doGiveFeedback              = doGiveFeedback;
  this.doTest                      = doTest;
  this.doPopupConvert              = doPopupConvert;
  this.observe = observe;
  this.prefChangedCallBack         = prefChangedCallBack;
  this.onTabClose                  = onTabClose;
  this.onTabSelect                 = onTabSelect;

  this._setTooltipMode             = _setTooltipMode;
  this._initGuiBeforeInstall       = _initGuiBeforeInstall;
  this._initGuiAfterInstall        = _initGuiAfterInstall;
  this._confirmInstalled           = _confirmInstalled;
  this._considerDoingInstall       = _considerDoingInstall;
  this._wizardStartup              = _wizardStartup;
  this._normalStartup              = _normalStartup;
  this._upgradedStartup            = _upgradedStartup;
  this._runStartupWizard           = _runStartupWizard;
  this._showUpgradedPopup          = _showUpgradedPopup;
  this._showWelcomePopup           = _showWelcomePopup;
  this.showConvertTooltip          = showConvertTooltip;  

  this.die   = die;
  var stPanel = VMC.StatusPanel.prototype;


  this.eventlisteners = null; // needs viewmycurrency before we can initalise

  this._initGuiBeforeInstall();
  
  function _initGuiBeforeInstall() {
    VMC.ge("VMC.statusbarpanel").addEventListener("mousedown",        function(e) { self.doStatusbarpanelClicked.call(self,e); }, false);
    stPanel.getActive().setStatus(stPanel.SETUP_WIZARD_RUNNING);
  }

  function _initGuiAfterInstall() {
    if (!this._confirmInstalled()) {
      VMC.alert('Please leave a comment at hamstersoup.com with the following error:\nTried to init whole gui before we were installed');
      return; //TODO: //set error status in pref?
    }
    VMC.log('DBG','Registering full gui');
    VMC.ge("contentAreaContextMenu").addEventListener("popupshowing",  function(e) { self.doContentMenuPopup.call(self,e);      }, false);
    VMC.ge("VMC.statusbarpanel").contextMenu="VMC.statusIconPopup2";

    this._prefManager.registerWatcher(this, [VMC.PrefManager.keys.HIDE_IN_STATUSBAR,
					     VMC.PrefManager.keys.TOOLTIP_MODE
					     ], 'prefChangedCallBack');

    this.prefChangedCallBack(VMC.PrefManager.keys.HIDE_IN_STATUSBAR); // fake a pref change to hide/show based on preferences
    this.prefChangedCallBack(VMC.PrefManager.keys.TOOLTIP_MODE);      // fake a pref change to add mouse listener if necessary
    this.doUpdateStatus(); // draw the inital gui

  }

  // let the object that does all the work register with this GUI object
  function registerViewMyCurrency(viewMyCurrency) {
    this._viewMyCurrency = viewMyCurrency;
    this._viewMyCurrency.updateGuiCallback = function(status,info) { self.doUpdateStatus(status,info); };    
    gMouse = new VMC.MouseHandler(viewMyCurrency,this.showConvertTooltip); // mousehandler needs a pointer to vmc. it's not addEventListener until needed
    this.eventlisteners = new EventListeners(viewMyCurrency);
    this._prefManager=viewMyCurrency.prefManager;

    var prefState=this._prefManager.determineStatusOfExistingPrefs();
    VMC.log('DBG','State of preferences: '+prefState);
    switch(prefState) {

    case VMC.PrefManager.stateVirgin:                   this._wizardStartup();     break; // cool. I got a new user of VMC!

    case VMC.PrefManager.stateCannotDetermineVersion:   
    case VMC.PrefManager.stateIncompatibleOlderVersion: 
    case VMC.PrefManager.stateCorrupt:                  this._wizardStartup(true); break; // old user, warn about deleting prefs

    case VMC.PrefManager.stateValidPrefsAndVmcUpgraded: this._upgradedStartup();   break; // prefs are OK but VMC has been updated. 
    case VMC.PrefManager.stateValidPrefsPresent:        this._normalStartup();     break; // no wizard required

    default : VMC.log('ERR','Unknown pref state '+prefState);
    }
  }

  function _upgradedStartup() {
    this._normalStartup(); //do the normal stuff
    this._prefManager.setCurrentVersion(); // write the new version into the prefs
    this._showUpgradedPopup(); // inform the user that this is a new version
  }

  function _normalStartup(wasWizardStartup) {
    //normal startup
    this._installStatus="ALREADY_INSTALLED";
    try {
      this._viewMyCurrency._initAfterInstall(wasWizardStartup);
    } catch (e) {
      if (wasWizardStartup) {
	VMC.alert('Oh dear! You just did a clean install but ViewMyCurrency prefs seems to be corrupt.\n'+e);
      } else {
	VMC.alert('It looks like your ViewMyCurrency preferences are corrupt.\n'+e);
	//WILL: this._wizardStartup(true); // try install again, warn about deleting prefs
      }
      //TODO: set disabled status??
    }

    this._viewMyCurrency.exchangeRateCache.webUpdater.addObserver(this);

    this._initGuiAfterInstall();
  }



  function _runStartupWizard(deletePrefs) {
    var retVal = {};
    window.openDialog( 'chrome://viewmycurrency/content/WILL_StartUpDialog.xul','VMC.StartUpDialog','chrome,resizable,modal',
		       VMC.currencyDB.getCompleteList(),this._prefManager.getDefaultSourceListPref(),deletePrefs,retVal );
    if (retVal['newTargetCurrencyList']) {
      var newTargetCurrencyList=retVal['newTargetCurrencyList'];
      this._prefManager.storeInitialPrefs(newTargetCurrencyList,deletePrefs);

      return true; // wizard compelted OK
    } else {
      return false; // the user clicked cancel
    }
  }

  function _showUpgradedPopup() {
    VMC.updateAndAddHandlerToLinks();
    var messageLabel = VMC.ge('VMC-upgraded-message');
    messageLabel.value="Upgraded to "+VMC.VERSION;

    var statusbarpanel = VMC.ge('VMC.statusbarpanel');
    var myPopup = VMC.ge('VMC-upgraded-popup');
    myPopup.showPopup(statusbarpanel,-1,-1,'popup','topright','bottomright'); 
  }

  function _showWelcomePopup() {
    // Display the welcome popup
    var statusbarpanel = VMC.ge('VMC.statusbarpanel');
    var myPopup = VMC.ge('VMC-installed-popup');
    myPopup.showPopup(statusbarpanel,-1,-1,'popup','topright','bottomright');
  }

  //  WILL: TODO: I used to delay wizard, it is necessary? this._scheduleCall('delayedInstall',3*1000); 
  function _wizardStartup(deletePrefs) {
    var configOK = this._runStartupWizard(deletePrefs);
    //this._prefManager.doFirstTimeSetup();

    if (configOK) {
      this._normalStartup(true); //do a normal startup but run the webupdate immediately
      this._showWelcomePopup();  // highlight the fact that we live on the statusbar
    } else {
      this._installStatus="DONT_INSTALL";
    }
  }

  function die() {
    if (!this._confirmInstalled()) { return; }
    this._viewMyCurrency.die();
    this._viewMyCurrency.exchangeRateCache.webUpdater.removeObserver(this);
    //WILL: TODO: currently the VMC.PrefManager.unregister()  kills all watchers, so we just do that in _viewMyCurrency.die()
  }

  function doToggleConvert() {
    this._viewMyCurrency.changeToNextTargetCurrency();
  }

  function doHideSource() {
    this._viewMyCurrency.toggleHideOriginal();
  }

  
  function _confirmInstalled() {
    if (!this._installStatus) { return false; }
    return (this._installStatus == 'ALREADY_INSTALLED');
  }

  //WILL: TODO: I don't think I need all these states if I run the wizard without delay
  function _considerDoingInstall() {
    switch(this._installStatus) {
    case "ALREADY_INSTALLED": VMC.log('ERR','We are already installed!'); break;
    case "TO_INSTALL":        return; // install should happen soon (by timeout)
    case "DONT_INSTALL":     //the user cancelled that last setup wizard, but now he seems to want our functionality
      this.registerViewMyCurrency(this._viewMyCurrency); // fake the inital startup flow of control to run the wizard again.
      break;
    default:
      VMC.log('ERR','unknown install status :'+this._installStatus);
    }
  }


  /////////////////////////////
  function doStatusbarpanelClicked (event) {
    /////////////////////////////
    if (! this._confirmInstalled() ) { this._considerDoingInstall(); return; }
    
    if (event.button !== 0) { return; } //WILL: This wasn't a left-click so do nothing here (right-click brings up menu). TODO: test on mac and unix

    this.doToggleConvert();

  }


  ////////////////////////
  function doUpdateStatus (state,info) {
    //////////////////////

    //just update this status bar?
    switch(state) {
      case "START" :       stPanel.getActive().setStatus(stPanel.CONVERTPAGE_START);       return;
      case "END_CHANGES" : stPanel.getActive().setStatus(stPanel.CONVERTPAGE_END_CHANGES); return; 
      case "END_NO_CHNG" : stPanel.getActive().setStatus(stPanel.CONVERTPAGE_END_NO_CHNG); return;
      case "OFF"         : stPanel.getActive().setStatus(stPanel.CONVERTPAGE_OFF);         return; // user disabled conversion
      case "AVOID"       : stPanel.getActive().setStatus(stPanel.PAGE_AVOID);              return;
      case "PROFILE"     : stPanel.getActive().setProfile(info);                           return; // show new profile
    default : // or do a complete gui update?
    }

    var hide=this._viewMyCurrency.hideOriginalCurrency;
    VMC.ge('VMC.hideOriginal' ).setAttribute('checked',hide);
    VMC.ge('VMC.hideOriginal2').setAttribute('checked',hide);


    var target    =this._viewMyCurrency.getTargetCurrency();
    var nextTarget=this._viewMyCurrency.getNextTargetCurrency();

    var menuLabel;
    if (nextTarget) { menuLabel='Convert to '+nextTarget.code;  }
    else            { menuLabel='Convert OFF';                  }
    VMC.ge('VMC.cmd_toggleConvert').setAttribute('label',menuLabel);

    if (!target) {
      stPanel.getActive().setStatus(stPanel.CONVERTPAGE_OFF);
    } else {
      stPanel.getActive().setStatus(stPanel.CONVERTPAGE_ON, target);
    }
  }

  // used to observe the WebUpdater. //Note: this only deals with gui stuff. VMC is responsible for updating browsers
  function observe(status,info) {
    var statusbarpanel = VMC.ge("VMC.statusbarpanel");    
    var style;
    var tooltiptext;

    switch(status) {
    case 'START'        : stPanel.getActive().setStatus(stPanel.WEBUPDATE_START);             break;
    case 'END_ERRORS'   : stPanel.getActive().setStatus(stPanel.WEBUPDATE_END_ERRORS);        break;
    case 'END_OK'       : stPanel.getActive().setStatus(stPanel.WEBUPDATE_END_OK);            break;
    case 'RATE_ERROR'   : stPanel.getActive().setStatus(stPanel.WEBUPDATE_RATE_ERROR);        break;
    case 'RATE_UPDATED' : stPanel.getActive().setStatus(stPanel.WEBUPDATE_RATE_UPDATED,info); break;
    default             : VMC.log('ERR','got unknown webupdate status ['+status+']');
    }
  }

  function getSelectedText() {
    if (this.getBrowserSelection)         { return getBrowserSelection(100); } // Firefox 2
    // else Firefox 1.5
    var sel=nsContextMenu.prototype.searchSelected(100); // can't use gContextMenu because there isn't a menu showing
    if (sel) { return sel.toString(); }
    else { return null; }

    //if (gContextMenu.searchSelected) { return gContextMenu.searchSelected(100); } //Firefox 1.5
    //VMC.log('ERR','Could not find selected text function');
    //return undefined;
  }

  // the user right-clicked inside the broswer. Now we decided if we should we add our menuitem
  function doContentMenuPopup(event) {
    if (!this._confirmInstalled()) { VMC.log('ERR','Right click menu should not be here until wizard is finished'); }

    var menuitem;
    var showPopupConvert;
    var seltext;

    var sel = getSelectedText();
    if (sel) {
      seltext = sel.toString();
      showPopupConvert=true;
    }
    menuitem = VMC.ge('VMC.contentMenuitemP');
    if (showPopupConvert) {
      menuitem.label=LABEL_POPUP_CONVERT+' "'+VMC.shortenStr(seltext,16)+'"';
      menuitem.hidden=false;
    } else {
      menuitem.hidden=true;
    }

    //WILL: TODO: consider separator and order of menuitems
  }


  // helper func
  function _getSelectedTextRange() {
    var focusedWindow = document.commandDispatcher.focusedWindow;
    var selectionObj = focusedWindow.getSelection();      
    if (!selectionObj.rangeCount)  { return null; }
    if (selectionObj.rangeCount>1) { VMC.alert('warning selection rangeCount is '+selectionObj.rangeCount+' only looking at first item'); }
    return selectionObj.getRangeAt(0);
  }


  // the user click on our menuitem in either the right-click context menu of the browser, our statusbar, our tools menu or pressed keyboard shortcut
  function doPopupConvert() {
    // run the popup converter
    var range=_getSelectedTextRange();
    var focus = document.commandDispatcher.focusedWindow; // so we can restore focus when we hide the toolbar
    VMC.popupConvertManager = new VMC.PopupConvertToolbarManager(this._viewMyCurrency,range,focus);
  }
    

  function doContextMenu(menuitem) {
    
    if (menuitem.label.substr(0,LABEL_POPUP_CONVERT.length) != LABEL_POPUP_CONVERT) {
      VMC.log('ERR','Unknown menuitem '+menuitem.label);
      return;
    }

    this.doPopupConvert();

  }
  
  function doOpenOptionsDialog() {
    if (! this._confirmInstalled() ) { this._considerDoingInstall(); return; }
    window.openDialog('chrome://viewmycurrency/content/WILL_OptionsDialog.xul','VMC.OptionsDialog','chrome,resizable,modal');
    // This modifies preferences. Changes are detected and processed by ViewMyCurrency
  }
 
  function doGiveFeedback() {
    var url='';
    var selectedText='';
    try {
      url = gBrowser.getBrowserAtIndex(gBrowser.mTabContainer.selectedIndex).contentDocument.URL;
      selectedText = getSelectedText();
    } catch (e) { VMC.log('WARN','could not find current URL or selected text '+e); }
    
    window.openDialog('chrome://viewmycurrency/content/WILL_GiveFeedback.xul','VMC.GiveFeedback','chrome,resizable',
		      url, selectedText);
  }

  
  function prefChangedCallBack(prefName) {
    VMC.log('DBG','GUI pref changed: '+prefName);

    var keys=VMC.PrefManager.keys;

    switch(prefName) {
    case keys.HIDE_IN_STATUSBAR   : VMC.ge('VMC.statusbarpanel').hidden=this._prefManager.getHideInStatusbar(); break;
    case keys.TOOLTIP_MODE        : this._setTooltipMode(); break;
    default                       : VMC.log('ERR','Saw unexpected pref '+prefName+' change'); return;
    }
  }

  var gTooltipNode = document.documentElement; //VMC.ge('appcontent'); //BUG: tooltips don't always appear if you add them to appcontent
  var gTooltipX; // position for tooltip //BUG required because we put tooltip in a setTimeout
  var gTooltipY;
  var mouseoverTooltip        = VMC.ge('VMC-mouseover-tooltip');
  var mouseoverConvertedLabel         = VMC.ge('VMC-mouseoverConverted-label');
  var mouseoverConvertedCurrencyLabel = VMC.ge('VMC-mouseoverConvertedCurrency-label');
  var mouseoverOriginalLabel          = VMC.ge('VMC-mouseoverOriginal-label');
  var mouseoverOriginalCurrencyLabel  = VMC.ge('VMC-mouseoverOriginalCurrency-label');
  var lastTooltipX=0;
  var lastTooltipY=0; // if the text hasn't changed, only move the tooltip if the mouse has moved far enough away

  function _setTooltipMode() {
    var vmc = this._viewMyCurrency;
    var newTooltipMode = this._prefManager.getTooltipMode();
    if (newTooltipMode) { //from tooltips OFF to ON
	this.eventlisteners.removeTabEventListeners();
	this.eventlisteners.addMouseEventListeners();
    } else { // from tooltips ON to OFF
	this.eventlisteners.addTabEventListeners();
	this.eventlisteners.removeMouseEventListeners();
    }
    vmc.tooltipMode = newTooltipMode;
  }

  function showConvertTooltip(event) {
    var x=event.screenX;
    var y=event.screenY;

    // WILL: TODO: solve FF bug about positioning of tooltip
    // mouseoverConvertedLabel.value = '('+x+','+y+')';
    // mouseoverOriginalLabel.value  = '';
    // mouseoverTooltip.showPopup(document.documentElement,x,y,'tooltip',null,null);
    // return;

    var text = VMC.HtmlConversion.findTextUnderMouse(event);
    if (text) {
      var vmc = self._viewMyCurrency;
      var r = VMC.NumberConversion.prototype.currencyConvertStrForTooltip(text, vmc._targetCurrency, vmc.conversionProfileTable._activeProfileEntry.profile, vmc.getRateFunc); //HACK: horrible OOP + HACK: need to set active profile when page loads
    }
    if (!r) {
      mouseoverTooltip.hidePopup();
    } else {
      var dx = lastTooltipX - x;
      var dy = lastTooltipY - y;
      var dist = Math.round(Math.sqrt(dx*dx + dy*dy));
      lastTooltipX = x;
      lastTooltipY = y;

      //if the text hasn't changed, only move the tooltip if the mouse has moved far enough away
      if (mouseoverConvertedLabel.value == r.convertedText && mouseoverOriginalLabel.value == r.originalText && dist<3) {
	// we can reduce the amount of popups, this seems to help
	// it's the same tooltip, so don't bother moving it
	return;
      }
      //HACK: if (BUG) {mouseoverTooltip.hidePopup();      document.popupNode = null; }
      mouseoverConvertedLabel.value         = r.convertedText;
      mouseoverConvertedCurrencyLabel.value = r.convertedCurrency;
      mouseoverOriginalLabel.value          = r.originalText;
      mouseoverOriginalCurrencyLabel.value  = r.originalCurrency;
      
      var box = mouseoverConvertedLabel.boxObject;
      gTooltipX = x - Math.floor(box.width / 2);
      gTooltipY = y;
      //HACK: if (BUG) {document.popupNode = gTooltipNode; }
      //setTimeout( _showPopup, 0 ); //BUG
      _showPopup();
      //BUG: I'm using screen coordinates, so why do I have to specify element?
    }
  }

  function _showPopup(count) {
    count = count || 1;
    if (count>3) { VMC.HACK('Bad tooltip: _showPopup GAVE UP on attempt '+count); return; }
    mouseoverTooltip.showPopup(gTooltipNode,gTooltipX,gTooltipY,'tooltip',null,null);
    var tooltipBox = mouseoverTooltip.boxObject; //HACK: update?
    var   dx = gTooltipX-tooltipBox.screenX;
    var   dy = gTooltipY-tooltipBox.screenY;
    if (Math.abs(dx)>30 || Math.abs(dy)>30) {
      VMC.HACK('_showPopup() bad tooltip: new x,y=('+gTooltipX+','+gTooltipY+')\t'+
	       'box screen x,y = ('+tooltipBox.screenX+','+tooltipBox.screenY+')\t'+
	       'dx,dy=('+dx+','+dy+')' );
      mouseoverTooltip.hidePopup();
      //_showPopup(count+1); //HACK: try again to fix the position -
      lastTooltipY = -100;
      lastTooltipX = -100; //force a new tooltip on next mouse move. Maybe the next one will work?
    } else {
      if (count>1) {	VMC.HACK('_showPopup() SUCCEDED after '+count+' attempts');      }
    }
  }

  function doTest() {
    //this.doGiveFeedback();
    //this._showWelcomePopup();
  }

  function onTabClose(e) {
    delete gBrowser.selectedBrowser.VMC_statusPanel;
  }
  
  function onTabSelect(event)  {
    var browser = gBrowser.selectedBrowser;
    if (browser.VMC_convertedTo === undefined || browser.VMC_convertedTo !== this._viewMyCurrency._targetCurrency ) {
      this._viewMyCurrency.updateCurrentTab();
    } else {
      // page is already processed, simply update the GUI with our last status
      stPanel.getActive().redisplay();
    }
  }

  // private object
  var EventListeners = function(viewMyCurrency) {
    var onDOMContentLoaded = function(e) { viewMyCurrency._onDOMContentLoaded(e); };
    var onTabSelect        = function(e) { self.onTabSelect(e);                   };
    var onTabClose         = function(e) { self.onTabClose(e);                    };
    var onMousemove        = function(e) { gMouse.onMousemove(e);                 };
    this.addTabEventListeners = function() {
      //FF2 only tab stuff below. Could be back ported using http://developer.mozilla.org/en/docs/Code_snippets:Tabbed_browser
      var container = gBrowser.tabContainer; 
      gBrowser.addEventListener( "DOMContentLoaded", onDOMContentLoaded, false);
      container.addEventListener("TabSelect",        onTabSelect,        false);
      container.addEventListener("TabClose",         onTabClose,        false);

    };
    this.removeTabEventListeners = function() {
      var container = gBrowser.tabContainer;
      gBrowser.removeEventListener( "DOMContentLoaded", onDOMContentLoaded, false);
      container.removeEventListener("TabSelect",        onTabSelect,        false);
      container.removeEventListener("TabClose",         onTabClose,        false);
    };

    this.addMouseEventListeners = function() {
      gBrowser.addEventListener( "mousemove",        onMousemove,        false);
    };
    this.removeMouseEventListeners = function() {
      gBrowser.removeEventListener( "mousemove",     onMousemove,        false);
    };
      //container.addEventListener("TabClose", exampleTabRemoved, false);
      //WILL: TODO: container.removeEventListener("TabSelect", handleTabSelected, false);
      //WILL: TODO: add and remove this listeners as required rather than just prematurely returning if their function isn't needed
  };


}; // end of ViewMyCurrencyGui

VMC.MouseHandler = function(vmc,onmouseslowmove) {
  var self = this;

  // every mouse movement sets 'moved' to true
  this.onMousemove = function onMousemove(event) {   // BUG: this basically implmements missing event onmouseslowmove
    //dump('.'); // Mouse move event
    var dx = self.x - event.screenX;
    var dy = self.y - event.screenY;
    var dist = Math.round(Math.sqrt(dx*dx + dy*dy));
    self.x = event.screenX;
    self.y = event.screenY;

    if (dist<4) { // mouse is moving slowly
      onmouseslowmove(event);
    }
  };
  
}; // end of MouseHandler

VMC.StatusPanel = function() {
  this.statusbarpanel   = VMC.ge("VMC.statusbarpanel");
  this.statuspanelLabel = VMC.ge("VMC.statuspanelLabel");
  this.statuspanelIcon  = VMC.ge("VMC.statuspanelIcon");
  this.statuspanelProfile =  VMC.ge('VMC.statuspanelProfile');
  this._storeCurrentSettings();
};

VMC.StatusPanel.prototype = {
  SETUP_WIZARD_RUNNING    : 'SETUP_WIZARD_RUNNING',
  CONVERTPAGE_START       : 'CONVERTPAGE_START',
  CONVERTPAGE_END_CHANGES : 'CONVERTPAGE_END_CHANGES',
  CONVERTPAGE_END_NO_CHNG : 'CONVERTPAGE_END_NO_CHNG',
  CONVERTPAGE_OFF         : 'CONVERTPAGE_OFF',         // (user disabled conversion)
  PAGE_AVOID              : 'PAGE_AVOID',
  CONVERTPAGE_ON          : 'CONVERTPAGE_ON',         //  + target
  WEBUPDATE_START         : 'WEBUPDATE_START',
  WEBUPDATE_END_ERRORS    : 'WEBUPDATE_END_ERRORS',
  WEBUPDATE_END_OK        : 'WEBUPDATE_END_OK',
  WEBUPDATE_RATE_ERROR    : 'WEBUPDATE_RATE_ERROR',
  WEBUPDATE_RATE_UPDATED  : 'WEBUPDATE_RATE_UPDATED', // + info

  _storeCurrentSettings : function() {
    this.label       = this.statuspanelLabel.value;
    this.icon        = this.statuspanelIcon.src;
    this.style       = this.statuspanelLabel.getAttribute('style');
    this.tooltiptext = this.statusbarpanel.getAttribute('tooltiptext');
    this.profileText = this.statuspanelProfile.value;
  },
  
  getActive : function() {
    var browser = gBrowser.selectedBrowser;
    // if the current tab does not have a StatusPanel object then add one now
    if (browser.VMC_statusPanel === undefined) { browser.VMC_statusPanel = new VMC.StatusPanel(); }
    return browser.VMC_statusPanel;
  },

  setProfile : function(profileText) {
    if (profileText == 'Default') {  profileText = ''; } // do not show Default profile
    this.statuspanelProfile.value=profileText;
  },

  setStatus : function(status,info) {
    VMC.log('DBG','StatusPanel status: '+status+' '+info);
    //WILL: TODO: what status was this? case this.CONVERTPAGE_OFF         : this._set('OFF','color:green',null); break;
    switch(status) {
    case this.SETUP_WIZARD_RUNNING    : this._set('OFF','','ViewMyCurrency v'+VMC.VERSION+' setup wizard is running','disabled'); break;
    case this.CONVERTPAGE_START       : this._set(null, 'color:green; font-weight:bold', null, 'enabled');  break;
    case this.CONVERTPAGE_END_CHANGES : this._set(null, 'font-weight:bold', null , 'enabled'); break;
    case this.CONVERTPAGE_END_NO_CHNG : this._set(null, ' ', null, 'inactive'); break;
    case this.PAGE_AVOID              : this._set(null, 'color:grey','This page is in your excluded list', 'disabled' ); break; //special icon?
    case this.CONVERTPAGE_OFF         : this._set('OFF' ,'color:black','Not converting currencies. Click to change','disabled'); break;
    case this.CONVERTPAGE_ON          : this._set(info.code+' '+info.getHtml(), null, 'Converting to '+info.description, 'enabled' ); break;
    case this.WEBUPDATE_START         : this._set(null, 'width:10em; color:blue;', 'Getting the latest exchange rates from web'); break; //icons??
    case this.WEBUPDATE_END_ERRORS    : this._set(null, 'color:red;',  'Failed to download all exchange rates'); break;
    case this.WEBUPDATE_END_OK        : this._set(null, '',            'Downloaded exchange rates OK');               break;
    case this.WEBUPDATE_RATE_ERROR    : this._set(null, 'color:darkred;','Failed to download exchange rate. (Skipped)'); break;
    case this.WEBUPDATE_RATE_UPDATED  : this._set(info, 'color:blue;', 'Getting the latest exchange rates from web'); break;
    default : VMC.log('ERR','Unknown StatusPanel status :'+status);
    }
  },
  
  // label is optional
  _set : function(label,style,tooltiptext,icon) {
    if (label)       { this.statuspanelLabel.value = label; }
    if (icon)        { this.statuspanelIcon.src="chrome://viewmycurrency/skin/" + icon + "_icon16.png"; }
    if (tooltiptext) { this._setTooltip(tooltiptext); }
    if (style)       { this.statuspanelLabel.setAttribute('style',style); }  //WILL: TODO: use style selectors
    this._storeCurrentSettings();
  },

  _setTooltip : function(tooltiptext) {
      this.statusbarpanel.setAttribute('tooltiptext',tooltiptext);
      this.statuspanelLabel.setAttribute('tooltiptext',tooltiptext); //WILL: Bug in FF, tooltips aren't inherited. 
      this.statuspanelIcon.setAttribute('tooltiptext',tooltiptext); //WILL: Bug in FF, tooltips aren't inherited. 
  },
    
  // reset back to last stored values (used for restoring status for current tab)
  redisplay : function() {
    this.statuspanelLabel.value = this.label;
    this.statuspanelIcon.src=this.icon;
    this._setTooltip(this.tooltiptext);
    this.statuspanelLabel.setAttribute('style',this.style);
    this.statuspanelProfile.value = this.profileText;
  }    
  
}; // end of StatusPanel

